package org.fioma.ui;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.fioma.general.MatchLog;
import org.fioma.general.Read;
import org.fioma.general.ReadStrings;

public class ExtractFastaFromLog {

	public static long case1 = 0, case2 = 0, case3 = 0;
	public static int k, c, j, s, e;
	public static int[] junctionsPos = new int[100];

	public static void main(String[] args) {

		try {
			long timeInic = System.currentTimeMillis();
			String matePairsFileName = args[0];
			String logExtension = args[1];
			String newLogExtension = args[2];
			k = Integer.parseInt(args[3]);
			c = Integer.parseInt(args[4]);
			j = Integer.parseInt(args[5]);
			s = Integer.parseInt(args[6]);
			e = Integer.parseInt(args[7]);

			System.out.print("Arguments:");
			for (String arg : args) {
				System.out.print(" " + arg);
			}
			System.out.println();

			matePairsHandler(matePairsFileName, logExtension, newLogExtension);

			System.out.println("matches:" + case1);
			System.out.println("junctions:" + case2);
			System.out.println("gaps:" + case3);
			System.out.println();
			System.out.println("Time: " + (System.currentTimeMillis() - timeInic));
			System.out.println();
			System.out.println("Junctions position:");
			for (int i = 0; i < junctionsPos.length; i++) {
				System.out.println("" + i + ": " + junctionsPos[i]);
				System.out.println();
			}

		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
		}
	}

	public static void matePairsHandler(String fileName, String extension, String newLogExtension) throws Exception {
		BufferedReader matePairsReads = new BufferedReader(new FileReader(fileName));

		String line;

		String file1, file2;
		String[] files;
		String line1File1;
		String line2File1;
		String line3File1;
		String line4File1;
		String line1File2;
		String line2File2;
		String line3File2;
		String line4File2;

		String lineLog1, lineLog2;
		String[] logs1, logs2;
		String[] matchesStr1, matchesStr1a, matchesStr2, matchesStr2a;

		ArrayList<MatchLog> matches1 = new ArrayList<MatchLog>(50);
		ArrayList<MatchLog> matches1a = new ArrayList<MatchLog>(50);
		ArrayList<MatchLog> matches2 = new ArrayList<MatchLog>(50);
		ArrayList<MatchLog> matches2a = new ArrayList<MatchLog>(50);

		Read read1, read2;

		List<Integer> gaps1 = new ArrayList<Integer>(10), gaps1a = new ArrayList<Integer>(10), gaps2 = new ArrayList<Integer>(10), gaps2a = new ArrayList<Integer>(
				10);
		List<Integer> junctions1 = new ArrayList<Integer>(10), junctions1a = new ArrayList<Integer>(10), junctions2 = new ArrayList<Integer>(10), junctions2a = new ArrayList<Integer>(
				10);

		while ((line = matePairsReads.readLine()) != null) {
			files = line.split(";");
			file1 = files[0];
			file2 = files[1];
			System.out.println("Starting the mate pair files:" + line);

			BufferedReader matePairsFile1 = new BufferedReader(new FileReader(file1));
			BufferedReader matePairsFile2 = new BufferedReader(new FileReader(file2));
			BufferedReader matePairsFile1Log = new BufferedReader(new FileReader(file1 + extension));
			matePairsFile1Log.readLine(); // read the header
			BufferedReader matePairsFile2Log = new BufferedReader(new FileReader(file2 + extension));
			matePairsFile2Log.readLine(); // read the header

			BufferedWriter matePairsFile1Gaps = new BufferedWriter(new FileWriter(file1 + newLogExtension + ".gaps.fasta", false));
			BufferedWriter matePairsFile2Gaps = new BufferedWriter(new FileWriter(file2 + newLogExtension + ".gaps.fasta", false));
			BufferedWriter matePairsFile1Junction = new BufferedWriter(new FileWriter(file1 + newLogExtension + ".junction.fasta", false));
			BufferedWriter matePairsFile2Junction = new BufferedWriter(new FileWriter(file2 + newLogExtension + ".junction.fasta", false));
			BufferedWriter matePairsFile1Match = new BufferedWriter(new FileWriter(file1 + newLogExtension + ".match.fasta", false));
			BufferedWriter matePairsFile2Match = new BufferedWriter(new FileWriter(file2 + newLogExtension + ".match.fasta", false));

			while ((lineLog1 = matePairsFile1Log.readLine()) != null) {
				lineLog2 = matePairsFile2Log.readLine();
				line1File1 = matePairsFile1.readLine();
				line2File1 = matePairsFile1.readLine();
				line3File1 = matePairsFile1.readLine();
				line4File1 = matePairsFile1.readLine();

				line1File2 = matePairsFile2.readLine();
				line2File2 = matePairsFile2.readLine();
				line3File2 = matePairsFile2.readLine();
				line4File2 = matePairsFile2.readLine();

				logs1 = lineLog1.split("\t");
				logs2 = lineLog2.split("\t");

				read1 = new ReadStrings(line1File1, line2File1, line4File1);
				read2 = new ReadStrings(line1File2, line2File2, line4File2);

				if (logs1.length < 5) {
					if (logs1.length > 0) {
						throw new Exception("Error in the read:" + logs1[0]);
					} else {
						throw new Exception("Error in the read:" + line1File1);
					}
				}
				if (logs2.length < 5) {
					if (logs2.length > 0) {
						throw new Exception("Error in the read:" + logs2[0]);
					} else {
						throw new Exception("Error in the read:" + line1File2);
					}
				}
				if (!read1.getId().equals(logs1[0]) || !read2.getId().equals(logs2[0])) {
					throw new Exception("Error in the reads ID");
				}

				matchesStr1 = logs1[3].substring(1, logs1[3].length() - 1).split("\\)\\(");
				matchesStr1a = logs1[4].substring(1, logs1[4].length() - 1).split("\\)\\(");
				matchesStr2 = logs2[3].substring(1, logs2[3].length() - 1).split("\\)\\(");
				matchesStr2a = logs2[4].substring(1, logs2[4].length() - 1).split("\\)\\(");

				matches1.clear();
				matches1a.clear();
				matches2.clear();
				matches2a.clear();

				gaps1.clear();
				gaps1a.clear();
				gaps2.clear();
				gaps2a.clear();

				junctions1.clear();
				junctions1a.clear();
				junctions2.clear();
				junctions2a.clear();

				fillMatches(matchesStr1, matches1, read1.getId(), read1.getLength());
				fillMatches(matchesStr1a, matches1a, read1.getId(), read1.getLength());
				fillMatches(matchesStr2, matches2, read2.getId(), read1.getLength());
				fillMatches(matchesStr2a, matches2a, read2.getId(), read1.getLength());

				getGapsInverted(matches1, gaps1);
				getGaps(matches1a, gaps1a);
				getGapsInverted(matches2, gaps2);
				getGaps(matches2a, gaps2a);

				getJunctionsInverted(matches1, junctions1);
				getJunctions(matches1a, junctions1a);
				getJunctionsInverted(matches2, junctions2);
				getJunctions(matches2a, junctions2a);

				printMatePairResult(read1, gaps1, gaps1a, junctions1, junctions1a, matePairsFile1Gaps, matePairsFile1Junction, matePairsFile1Match);
				printMatePairResult(read2, gaps2, gaps2a, junctions2, junctions2a, matePairsFile2Gaps, matePairsFile2Junction, matePairsFile2Match);

				MatchLog.freePool();
			}
			matePairsFile1.close();
			matePairsFile2.close();
			matePairsFile1Log.close();
			matePairsFile2Log.close();
			matePairsFile1Gaps.flush();
			matePairsFile1Gaps.close();
			matePairsFile2Gaps.flush();
			matePairsFile2Gaps.close();

			matePairsFile1Junction.flush();
			matePairsFile1Junction.close();
			matePairsFile2Junction.flush();
			matePairsFile2Junction.close();

			matePairsFile1Match.flush();
			matePairsFile1Match.close();
			matePairsFile2Match.flush();
			matePairsFile2Match.close();
		}
		matePairsReads.close();
	}

	private static void getJunctions(ArrayList<MatchLog> matches, List<Integer> junctions) {
		if (matches.size() > 1) {
			MatchLog match = matches.get(0);
			int lastEnd = match.getMatePairEnd();
			int start;
			for (int i = 1; i < matches.size(); i++) {
				match = matches.get(i);
				start = match.getMatePairStart();
				if (start >= lastEnd - c && start <= lastEnd + j) {
					junctions.add(lastEnd);
				}
				lastEnd = match.getMatePairEnd();
			}
		}
	}

	private static void getJunctionsInverted(ArrayList<MatchLog> matches, List<Integer> junctions) {
		if (matches.size() > 1) {
			MatchLog match = matches.get(0);
			int lastStart = match.getMatePairStart();
			int end;
			for (int i = 1; i < matches.size(); i++) {
				match = matches.get(i);
				end = match.getMatePairEnd();
				if (end <= lastStart + c && end >= lastStart - j) {
					junctions.add(lastStart);
				}
				lastStart = match.getMatePairStart();
			}
		}
	}

	private static void getGaps(ArrayList<MatchLog> matches, List<Integer> gaps) {
		if (matches.size() > 0) {
			MatchLog match = matches.get(0);
			if (match.getMatePairStart() > s) {
				gaps.add(match.getMatePairStart());
			}
			int lastEnd = match.getMatePairEnd();
			int start;
			for (int i = 1; i < matches.size(); i++) {
				match = matches.get(i);
				start = match.getMatePairStart();
				if (start > lastEnd + j) {
					gaps.add(start);
				}
				lastEnd = match.getMatePairEnd();
			}
			if (lastEnd < match.getMatePairSize() - e) {
				gaps.add(lastEnd);
			}
		}
	}

	private static void getGapsInverted(ArrayList<MatchLog> matches, List<Integer> gaps) {
		if (matches.size() > 0) {
			MatchLog match = matches.get(0);
			if (match.getMatePairEnd() < match.getMatePairSize() - e) {
				gaps.add(match.getMatePairEnd());
			}
			int lastStart = match.getMatePairStart();
			int end;
			for (int i = 1; i < matches.size(); i++) {
				match = matches.get(i);
				end = match.getMatePairEnd();
				if (end < lastStart - j) {
					gaps.add(end);
				}
				lastStart = match.getMatePairStart();
			}
			if (lastStart > s) {
				gaps.add(lastStart);
			}
		}
	}

	private static void fillMatches(String[] matchesStr, ArrayList<MatchLog> matches, String readId, int readLength) throws Exception {
		String[] matchFields;
		for (String str : matchesStr) {
			matchFields = str.split(" ");
			if (matchFields.length < 5) {
				throw new Exception("Error in the match fields. Read:" + readId);
			}
			if (Integer.parseInt(matchFields[1]) >= k) {
				matches.add(MatchLog.getInstance(matchFields[0], matchFields[1], readLength, matchFields[2], matchFields[3], matchFields[4]));
			}
		}

	}

	private static void printMatePairResult(Read read, List<Integer> gaps, List<Integer> gapsa, List<Integer> junctions, List<Integer> junctionsa,
			BufferedWriter matePairsFileGaps, BufferedWriter matePairsFileJunction, BufferedWriter matePairsFileMatch) throws IOException {
		if ((gaps.isEmpty() && junctions.isEmpty()) || (gapsa.isEmpty() && junctionsa.isEmpty())) {
			matePairsFileMatch.write(read.getId().replace('@', '>'));

			matePairsFileMatch.write("|");
			for (int i : junctions) {
				matePairsFileMatch.write(i + " ");
			}
			matePairsFileMatch.write("|");
			for (int i : junctionsa) {
				matePairsFileMatch.write(i + " ");
			}
			matePairsFileMatch.write("|");
			for (int i : gaps) {
				matePairsFileMatch.write(i + " ");
			}
			matePairsFileMatch.write("|");
			for (int i : gapsa) {
				matePairsFileMatch.write(i + " ");
			}

			matePairsFileMatch.newLine();
			matePairsFileMatch.write(read.getSequence());
			matePairsFileMatch.newLine();
			case1++;
		} else if ((gaps.isEmpty() && junctions.size() == 1) || (gapsa.isEmpty() && junctionsa.size() == 1)) {
			matePairsFileJunction.write(read.getId().replace('@', '>'));
			matePairsFileJunction.write("|");
			for (int i : junctions) {
				matePairsFileJunction.write(i + " ");
			}
			matePairsFileJunction.write("|");
			for (int i : junctionsa) {
				matePairsFileJunction.write(i + " ");
			}
			matePairsFileJunction.write("|");
			for (int i : gaps) {
				matePairsFileJunction.write(i + " ");
			}
			matePairsFileJunction.write("|");
			for (int i : gapsa) {
				matePairsFileJunction.write(i + " ");
			}

			int junction = 0;
			if (!junctions.isEmpty()) {
				junction = junctions.get(0);
			} else if (!junctionsa.isEmpty()) {
				junction = junctionsa.get(0);
			}
			matePairsFileJunction.newLine();
			matePairsFileJunction.write(read.getSequence().substring(0, junction + 1));
			matePairsFileJunction.newLine();
			junctionsPos[junction]++;
			case2++;
		} else {
			// the mate pair doesn't have a good alignment (gaps or many
			// junctions)
			matePairsFileGaps.write(read.getId().replace('@', '>'));
			matePairsFileGaps.write("|");
			for (int i : junctions) {
				matePairsFileGaps.write(i + " ");
			}
			matePairsFileGaps.write("|");
			for (int i : junctionsa) {
				matePairsFileGaps.write(i + " ");
			}
			matePairsFileGaps.write("|");
			for (int i : gaps) {
				matePairsFileGaps.write(i + " ");
			}
			matePairsFileGaps.write("|");
			for (int i : gapsa) {
				matePairsFileGaps.write(i + " ");
			}
			matePairsFileGaps.newLine();
			matePairsFileGaps.write(read.getSequence());
			matePairsFileGaps.newLine();
			case3++;
		}
	}

	private static void printMatePairFasta(Read read, BufferedWriter matePairsFileResult) throws IOException {
		matePairsFileResult.write(read.getId().replace('@', '>'));
		matePairsFileResult.newLine();
		matePairsFileResult.write(read.getSequence());
		matePairsFileResult.newLine();
	}

}
